package hh.util;

import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import hh.Controller;
import hh.util.bean.ClassInfo;
import hh.util.bean.ReflectFieldInfo;
import hh.util.coll.CollUtil;
import hh.util.time.TimeUtil;

import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 简单常用工具,定义为不需要依赖额外jar包(或者常用的如spring apache common),如果需要依赖额外jar包,应该放在ServiceUtil中<br>
 * 在这个类中,你应该导入的都是jdk自带的类
 *
 * @author HuangHuai on 2019-06-27 11:30
 **/
@SuppressWarnings({"unused", "unchecked"})
public class CommonUtil implements Serializable {

    private static final long serialVersionUID = 7816069471546717762L;
    private static Log log = Log.instance;
    /**
     * 仅仅用来：json和对象的互转 其他的，如需要设置忽略以及设置json格式等操作，统统不允许使用此对象，应当自己new一个<br/>
     */
    public static final ObjectMapper jsonMapper;

    static {
        ObjectMapper mapper = null;
        try {
            mapper = new ObjectMapper().setDateFormat(new SimpleDateFormat(TimeUtil.YYYY_MM_DD_HH_MM_SS_))
                    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                    .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS.mappedFeature(), true);
        } catch (Throwable e) {
            e.printStackTrace();
            mapper = null;
        }
        jsonMapper = mapper;
    }

    /**
     * 把json字符串转为指定对象
     */
    public static <T> T json2Obj(String jsonString, Class<T> desClass) {
        try {
            return jsonMapper.readValue(jsonString, desClass);
        } catch (Exception e) {
            String msg = format("Deserialize json to Type:{} failed. Cause:{}\n{}",
                    desClass.getName(), e.getMessage(), jsonString);
            throw new IllegalArgumentException(msg, e);
        }
    }

    /**
     * usage: <br>
     * <blockquote><pre>
     *         String jsonString = "{...}";
     *
     *         TypeReference typeReference = new TypeReference&lt;ResultView&lt;User&gt;&gt;(){};
     *
     *         ResultView&lt;User&gt; rv = json2Obj(jsonString, typeReference);
     *     </pre></blockquote>
     */
    public static <T> T json2Obj(String jsonString, TypeReference<T> typeReference) {
        try {
            JavaType javaType = TypeFactory.defaultInstance().constructType(typeReference);
            return jsonMapper.readValue(jsonString, javaType);
        } catch (Exception e) {
            String msg = format("Deserialize json to Type:{} failed. Cause:{}\n{}",
                    typeReference.getType().getTypeName(), e.getMessage(), jsonString);
            throw new IllegalArgumentException(msg, e);
        }
    }

    /**
     * 把对象转为json字符串
     */
    public static String obj2Json(Object obj) {
        try {
            return jsonMapper.writeValueAsString(obj);
        } catch (Exception e) {
            String msg = format("Serialize Type:{} to json failed. Cause:{}",
                    obj.getClass().getName(), e.getMessage());
            throw new IllegalArgumentException(msg, e);
        }
    }

    public static boolean isNotEmpty(Object o) {
        return !isEmpty(o);
    }

    /**
     *
     */
    public static boolean isEmpty(Object o) {
        if (o == null) return true;
        if (o instanceof CharSequence) {
            String trim = o.toString().trim();
            return trim.length() == 0 || trim.equalsIgnoreCase("null");
        }
        if (o instanceof Collection) return ((Collection) o).size() == 0;
        if (o instanceof Map) return ((Map) o).size() == 0;
        if (o.getClass().isArray()) return Array.getLength(o) == 0;
        return false;
    }

    /**
     * <blockquote><pre>
     *     List<User> list = getResult();
     *     if(CollectionUtil.isNotEmpty(list)){
     *         User user = list.get(0);
     *         if(user.getName()!=null){
     *             modifyName(user.getName());
     *         }
     *
     *     }
     *
     *     上面代码可以被替代为：
     *
     *     if(!isEmpty(() -> list.get(0).getName()!=null){
     *         modifyName(list.get(0).getName());
     *     }
     *
     *
     * </pre></blockquote>
     */
    public static boolean isEmpty(Fun.F01Throw<?> fun) {
        try {
            return isEmpty(fun.apply());
        } catch (Exception e) {
            return true; //报错说明空指针了 肯定是空
        }
    }
    public static boolean isNotEmpty(Fun.F01Throw<?> fun) {
        return !isEmpty(fun);
    }

    public static boolean isStringEmpty(String str) {
        return StringUtil.isEmpty(str);
    }

    public static boolean isStringNotEmpty(String str) {
        return !isStringEmpty(str);
    }


    public static boolean isMapEmpty(Map<?, ?> m) {
        return m == null || m.isEmpty();
    }

    public static boolean isMapNotEmpty(Map<?, ?> m) {
        return !isMapEmpty(m);
    }

    public static boolean isCollectionEmpty(Collection<?> m) {
        return m == null || m.isEmpty();
    }

    public static boolean isCollectionNotEmpty(Collection<?> m) {
        return !isCollectionEmpty(m);
    }

    public static void main(String[] args) {
        String s = "getName";
        ClassInfo classInfo = new ClassInfo(Controller.class);

        ReflectFieldInfo name = classInfo.getFieldInfo("n"+"ame");
        System.out.println(name.getGetter().getName()==("get"+"Name").intern());
        System.out.println(name.getSetter().getName()==s);
    }

    public static boolean oneIsEmpty(Object... args) {
        if (args == null) return true;
        for (Object arg : args)
            if (isEmpty(arg)) return true;
        return false;
    }

    public static boolean allIsEmpty(Object... args) {
        if (args == null) return true;
        for (Object arg : args) {
            if (isNotEmpty(arg)) return false;
        }
        return true;
    }

    public static boolean oneIsNull(Object... args) {
        if (args == null) return true;
        for (Object arg : args)
            if (arg == null) return true;
        return false;
    }

    public static boolean allIsNotEmpty(Object... args) {
        return !oneIsEmpty(args);
    }


    public static boolean allIsNull(Object... args) {
        if (args == null) return true;
        for (Object arg : args) {
            if (arg != null) return false;
        }
        return true;
    }

    public static boolean allIsNotNull(Object... args) {
        if (args == null) return false;
        for (Object arg : args) {
            if (arg == null) return false;
        }
        return true;
    }


    public static void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (Exception e) {
        }
    }

    /**
     *
     */
    public static String mkString(Object... arr) {
        return mkString("", ",", "", arr);
    }


    /**
     * <blockquote><pre>
     *     mkString("[", ",", "]","张三","李雷","韩梅梅") ==> [张三,李雷,韩梅梅]
     * </pre></blockquote>
     */
    public static String mkString(String pre, String middle, String suffix, Object... arr) {
        StringBuilder sb = new StringBuilder(100);
        sb.append(pre);
        if (arr != null) {
            for (int i = 0; i < arr.length; i++) {
                if (i != 0) {
                    sb.append(middle);
                }
                sb.append(arr[i]);
            }
        }
        sb.append(suffix);
        return sb.toString();
    }

    public static String exceptionToString(Throwable e) {
        if (e == null)
            return "";
        Bout o = new Bout(5000);
        e.printStackTrace(new PrintStream(o));
        return o.toString();
    }

    // 本方法属于0层，用户属于1层，但是用户希望传递 0 代表自身，1代表上层，-1代表下层
    // stackTrace是栈结构的，new Throwable()发生的方法首先入栈，所以本方法是0号元素
    // 一般方法调用者,即有上层,也有下层,0层没有人会使用,所以次方法是private的,那么你传递的必然不是0
    public static String getLineInfo(int depth) {
        return new Throwable().getStackTrace()[depth + 1].toString();
    }

    public static StackTraceElement getStackTraceElement(int depth) {
        return new Throwable().getStackTrace()[depth + 1];
    }

    // --------------------------- io -----------------------------------------

    /**
     * 关流
     */
    public static void closeQuietly(AutoCloseable... closeables) {
        if (closeables != null) {
            for (AutoCloseable resource : closeables)
                closeQuietly(false, resource);
        }
    }

    public static void closeQuietly(AutoCloseable closeables) {
        closeQuietly(false, closeables);
    }

    public static void closeQuietly(boolean needLineInfo, AutoCloseable resource) {
        if (resource != null) {
            try {
                resource.close();
                if (needLineInfo)
                    log.info("Closed {} at {}", resource.getClass().getName(), getLineInfo(1));
            } catch (Exception e) {
            }
        }
    }

    //-----------------------------------------

    /**
     * 计算耗时 格式： Takes time: xxx s xxx ms;
     */
    public static String countTime(long startTime) {
        long countTime = System.currentTimeMillis() - startTime;
        return format("Takes time: {}s {}ms;", countTime / 1000, countTime % 1000);
    }


    /**
     * Braces means { <br>
     * e.g: <br>
     * <blockquote><pre>
     *    formatBraces("select * from {} where {} = {}" , "user", "age", 18);
     *    result:  select * from user where age = 18;
     *  </blockquote></pre>
     *
     * <blockquote><pre>
     *    formatBraces("select * from {table} where {field} = {value}" , "user", "age", 18);
     *    result:  select * from user where age = 18;
     *  </blockquote></pre>
     *
     * <br>
     * formatBraces意味着参数占位符是大括号  {}
     */
    public static String format(String messagePattern, Object... argArray) {
        return StringUtil.format(messagePattern, argArray);
    }


    //	-------------------- 以下方法快速创建一个集合, 省去了new 操作 以及add put操作 -------------------------------------------

    public static <T> List<T> newArrayList(T... values) {
        return CollUtil.newArrayList(values);
    }

    public static <T> Set<T> newHashSet(T... values) {
        return CollUtil.newHashSet(values);
    }

    public static <K, V> Map<K, V> newHashMap(K key, V value) {
        return CollUtil.newHashMap(key, value);
    }

    public static Map<?, ?> newTreeMapKvs(Object... kvs) {
        return CollUtil.newTreeMapKvs(kvs);
    }

    public static Map<?, ?> newHashMapKvs(Object... kvs) {
        return CollUtil.newHashMapKvs(kvs);
    }

    public static Map<?, ?> newHashMapKeys(Object... kvs) {
        return CollUtil.newHashMapKeys(kvs);
    }

    public static <K, V> Map<K, V> newHashMap(K key, V value, int capacity) {
        return CollUtil.newHashMap(key, value, capacity);
    }

    // ------------------- class相关 -------------------------

    public static String getClassPath(Class<?> clazz) {
        return IoUtil.getClassPath(clazz);
    }

    public static String getClassPath(Class<?> clazz, String path) {
        return IoUtil.getClassPath(clazz) + path;
    }

    public static String getResourcesPath(Class<?> clazz) {
        return IoUtil.getResourcesPath(clazz);
    }

    public static String getSrcPath(Class<?> clazz) {
        return IoUtil.cd(IoUtil.getResourcesPath(clazz), "../..");
    }


    // ------------------- assert 断言空 抛出异常 -------------------------


    public static Object getFromMap(Map<?, ?> map, String path) {
        return containFromMap(map, path).value;
    }

    public static Contain containFromMap(Map<?, ?> map, String path) {
        if (isMapEmpty(map)) {
            return Contain.noContain;
        }
        String[] split = path.split("\\.");
        Object d = map;
        for (String s : split) {
            if (d instanceof Map) {
                Object o = ((Map<?, ?>) d).get(s);
                if (o == null)
                    return Contain.noContain;
                d = o;
            } else
                return Contain.noContain;
        }
        return new Contain(true, d);
    }

    public static boolean eq(Number num, Number val) {
        if (num == null || val == null) {
            return false;
        }
        return num.equals(val);
    }

    public static String uuid() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    public static <K, V> V getOrPutThreadSafe(K key, Map<K, V> map, Fun.F11<K, V> elementCreator) {
        V v = map.get(key);
        if (v == null) {
            synchronized (map) {
                if ((v = map.get(key)) == null) {
                    map.put(key, v = elementCreator.apply(key));
                }
            }
        }
        return v;
    }

    public static void illegalArgumentException(String msg, Object... args) throws IllegalArgumentException {
        throw new IllegalArgumentException(format(msg, args));
    }

    public static boolean mkDirs(String s) {
        File file = new File(s);
        if (!file.exists())
            return file.mkdirs();
        return false;
    }

    public static <T> T deserialize(byte[] bytes, Class<T> type) {
        return (T) deserialize(bytes);
    }

    public static Object deserialize(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        try {
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
            return ois.readObject();
        } catch (IOException ex) {
            throw new IllegalArgumentException("Failed to deserialize object", ex);
        } catch (ClassNotFoundException ex) {
            throw new IllegalStateException("Failed to deserialize object type", ex);
        }
    }

    public static byte[] serialize(Object object) {
        if (object == null) {
            return null;
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            oos.flush();
        } catch (IOException ex) {
            throw new IllegalArgumentException("Failed to serialize object of type: " + object.getClass(), ex);
        }
        return baos.toByteArray();
    }

    public static Properties newProperties(String key, Object v) {
        Properties p = new Properties();
        p.setProperty(key, v.toString());
        return p;

    }

    static String pid;

    public static String getPid() {
        try {
            if (pid == null)
                pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
            return pid;
        } catch (Throwable ex) {
            return null;
        }
    }

    public static class Contain {
        private boolean contain;
        private Object value;
        private static final Contain noContain = new Contain(false);

        public Contain(boolean contain) {
            this.contain = contain;
        }

        public Contain(boolean contain, Object value) {
            this.contain = contain;
            this.value = value;
        }

        public boolean isContain() {
            return contain;
        }

        public Object getValue() {
            return value;
        }

        @Override
        public String toString() {
            return "{contains=" + contain + ", value=" + value + '}';
        }
    }


    private static class Bout extends ByteArrayOutputStream {
        public Bout(int size) {
            super(size);
        }

        @Override
        public byte[] toByteArray() {
            return this.buf;
        }

        public String toString() {
            return new String(this.buf, 0, count, StandardCharsets.UTF_8);
        }

        @Override
        public int size() {
            return count;
        }
    }

}
